package com.ddpt.gateway.filter;

import cn.hutool.crypto.digest.MD5;
import com.ddpt.gateway.filter.base.BaseLimitFilter;
import com.ddpt.gateway.filter.config.LimitConfiguration;
import com.ddpt.platform.common.constant.RedisPrefix;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 防抖限流
 * 同一个token访问同一个接口只会放行1个
 */
@Slf4j
@Component
@RefreshScope
public class OnceLimitFilter extends BaseLimitFilter implements GlobalFilter, Ordered {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private LimitConfiguration configuration;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        if(!configuration.isEnableLimit()){
            return chain.filter(exchange);
        }

        PathMatcher matcher = new AntPathMatcher();
        String path = exchange.getRequest().getURI().getPath();
        if(!CollectionUtils.isEmpty(configuration.getNotLimitUrl())){
            //判断当前url是否跳过限流
            if(configuration.getNotLimitUrl().stream().anyMatch(url -> matcher.match(url,path))){
                return chain.filter(exchange);
            }
        }

        Set<String> limitUrl = configuration.getLimitUrl();
        if(CollectionUtils.isEmpty(limitUrl)){
            return chain.filter(exchange);
        }
        if(limitUrl.stream().noneMatch(url -> matcher.match(url, path))){
            return chain.filter(exchange);
        }

        String token = exchange.getRequest().getHeaders().getFirst("token");
        //防重放处理,gateway只做简单校验，具体验签交给permission处理
        String tag = MD5.create().digestHex16(token + path);
        String val = stringRedisTemplate.opsForValue().get(RedisPrefix.ONCE_LIMIT_FILTER+tag);
        if(StringUtils.isBlank(val)){
            //5秒内，不允许此token访问该接口
            stringRedisTemplate.opsForValue().set(RedisPrefix.ONCE_LIMIT_FILTER+tag,"0",5, TimeUnit.SECONDS);
            //将该key传递给微服务，微服务可以在过期时间之前清理掉此key限制
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            builder.header(RedisPrefix.ONCE_LIMIT_FILTER, tag);
            return chain.filter(exchange.mutate().request(builder.build()).build());
        }
        log.warn("---用户[{}]频繁请求接口[{}]被拦截---",token,exchange.getRequest().getURI().getPath());
        return writeResponse(exchange,HttpStatus.valueOf(configuration.getLimitHttpStatus()),configuration.getLimitMsg());
    }

    @Override
    public int getOrder() {
        return -98;
    }
}
